
local slot = require[[slot]]

local mt = {}
mt.__index = mt

mt.new = function()
	if mt._io == nil then error("you must install at least one io") end
	return setmetatable({
		on_event = slot.new(),	-- agument is always (t, x)
		on_complete = slot.new(),
	}, mt)
end

mt.from_button_press = function()
	local s = mt.new()
	local identity = function(t, x) s.on_event:put(t, x) end
	mt._io.listen_button_press(identity)
	return s
end

mt.state = function(this, initial)
	return require[[state]].from_stream(this, initial)
end

mt.map = function(this, f)
	local s = mt.new()
	this.on_event(function(t, x)
		s.on_event:put(t, f(x))
	end)
	return s
end

mt.filter = function(this, pred)
	local s = mt.new()
	this.on_event(function(t, x)
		if pred(x) then s.on_event:put(t, x) end
	end)
	return s
end

mt.scan = function(this, f, value)
	local s = mt.new()
	this.on_event(function(t, x)
		value = f(value, x)
		s.on_event:put(t, value)
	end)
	return s
end

local merge
merge = function(listener, s, ...)
	if s == nil then return end
	s.on_event(listener)
	merge(listener, ...)
end
mt.merge = function(...)
	local s = mt.new()
	merge(function(t, x) s.on_event:put(t, x) end, ...)
	return s
end

mt.throttle = function(this, duration)
	local s = mt.new()
	local events = {}
	local timed_out = function(t)
		s.on_event:put(t, events)
		events = {}
	end
	this.on_event(function(t, x)
		events[1+#events] = x
		mt._io.timeout(timed_out, duration)
	end)
	return s
end

return mt

